#include "stdafx.h"
#include "Texture.h"

#include "Terrain.h"
#include "Particle.h"

#include "Enemy.h"

#include "C3DOjbect.h"

void DrawTargets();												// Declaration


// User Defined Variables
GLuint		base;												// Font Display List
GLfloat		roll;												// Rolling Clouds
GLint		level=1;											// Current Level
GLint		miss;												// Missed Targets
GLint		kills;												// Level Kill Counter
GLint		score;												// Current Score
bool		game;												// Game Over?

TextureImage textures[10];										// Storage For 10 Textures

Enemy enemy[30];												// Storage For 30 Objects

C3DOjbect	gun;
C3DOjbect	bee[3];

extern int mouse_x, mouse_y;
extern int window_x, window_y;
extern float gunangle;


GLvoid BuildFont(GLvoid)										// Build Our Font Display List
{
	base=glGenLists(95);										// Creating 95 Display Lists
	glBindTexture(GL_TEXTURE_2D, textures[9].texID);			// Bind Our Font Texture
	for (int loop=0; loop<95; loop++)							// Loop Through All 95 Lists
	{
		float cx=float(loop%16)/16.0f;							// X Position Of Current Character
		float cy=float(loop/16)/8.0f;							// Y Position Of Current Character

		glNewList(base+loop,GL_COMPILE);						// Start Building A List
			glBegin(GL_QUADS);									// Use A Quad For Each Character
				glTexCoord2f(cx,         1.0f-cy-0.120f); glVertex2i(0,0);	// Texture / Vertex Coord (Bottom Left)
				glTexCoord2f(cx+0.0625f, 1.0f-cy-0.120f); glVertex2i(16,0);	// Texutre / Vertex Coord (Bottom Right)
				glTexCoord2f(cx+0.0625f, 1.0f-cy);		  glVertex2i(16,16);// Texture / Vertex Coord (Top Right)
				glTexCoord2f(cx,         1.0f-cy);		  glVertex2i(0,16);	// Texture / Vertex Coord (Top Left)
			glEnd();											// Done Building Our Quad (Character)
			glTranslated(10,0,0);								// Move To The Right Of The Character
		glEndList();											// Done Building The Display List
	}															// Loop Until All 256 Are Built
}

GLvoid glPrint(GLint x, GLint y, const char *string, ...)		// Where The Printing Happens
{
	char		text[256];										// Holds Our String
	va_list		ap;												// Pointer To List Of Arguments

	if (string == NULL)											// If There's No Text
		return;													// Do Nothing

	va_start(ap, string);										// Parses The String For Variables
	    vsprintf(text, string, ap);								// And Converts Symbols To Actual Numbers
	va_end(ap);													// Results Are Stored In Text

	glBindTexture(GL_TEXTURE_2D, textures[9].texID);			// Select Our Font Texture
	glPushMatrix();												// Store The Modelview Matrix
	glLoadIdentity();											// Reset The Modelview Matrix
	glTranslated(x,y,0);										// Position The Text (0,0 - Bottom Left)
	glListBase(base-32);										// Choose The Font Set
	glCallLists(strlen(text), GL_UNSIGNED_BYTE, text);			// Draws The Display List Text
	glPopMatrix();												// Restore The Old Projection Matrix
}

BOOL Initialize ()					// Any OpenGL Initialization Goes Here
{
	srand( (unsigned)time( NULL ) );							// Randomize Things

	if ((!LoadTGA(&textures[0],"Data/BlueFace.tga")) ||			// Load The BlueFace Texture
		(!LoadTGA(&textures[1],"Data/Bucket.tga")) ||			// Load The Bucket Texture
		(!LoadTGA(&textures[2],"Data/Target.tga")) ||			// Load The Target Texture
		(!LoadTGA(&textures[3],"Data/Coke.tga")) ||				// Load The Coke Texture
		(!LoadTGA(&textures[4],"Data/Vase.tga")) ||				// Load The Vase Texture
		(!LoadTGA(&textures[5],"Data/Explode.tga")) ||			// Load The Explosion Texture
		(!LoadTGA(&textures[6],"Data/Ground.tga")) ||			// Load The Ground Texture
		(!LoadTGA(&textures[7],"Data/Sky.tga")) ||				// Load The Sky Texture
		(!LoadTGA(&textures[8],"Data/Crosshair.tga")) ||		// Load The Crosshair Texture
		(!LoadTGA(&textures[9],"Data/Font.tga")))				// Load The Crosshair Texture
	{
		return FALSE;											// If Loading Failed, Return False
	}

	BuildFont();												// Build Our Font Display List

	glClearColor (0.0f, 0.0f, 0.0f, 0.5f);						// Black Background
	glClearDepth (1.0f);										// Depth Buffer Setup
	glDepthFunc(GL_LEQUAL);										// Type Of Depth Testing
	glEnable(GL_DEPTH_TEST);									// Enable Depth Testing
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);			// Enable Alpha Blending (disable alpha testing)
	glEnable(GL_BLEND);											// Enable Blending       (disable alpha testing)
//	glAlphaFunc(GL_GREATER,0.1f);								// Set Alpha Testing     (disable blending)
//	glEnable(GL_ALPHA_TEST);									// Enable Alpha Testing  (disable blending)
	glEnable(GL_TEXTURE_2D);									// Enable Texture Mapping
	//glEnable(GL_CULL_FACE);										// Remove Back Face
	
	// Setup GL States
	glShadeModel (GL_SMOOTH);									// Select Smooth Shading
	glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);	// Really Nice Perspective Calculations
	glHint(GL_POINT_SMOOTH_HINT,GL_NICEST);				// Really Nice Point Smoothing

	bee[0].LoadObject( "bee4.obj", "bee4.mtl" );
	bee[1].LoadObject( "bee5.obj", "bee5.mtl" );
	bee[2].LoadObject( "bee6.obj", "bee6.mtl" );

	for (int loop=0; loop<30; loop++)							// Loop Through 30 Objects
	{
		enemy[loop].Init();
		enemy[loop].LoadObject( &bee[0] );
		enemy[loop].LoadObject( &bee[1] );
		enemy[loop].LoadObject( &bee[2] );
		enemy[loop].LoadObject( &bee[1] );
		enemy[loop].SetAnimationCycleTime( 500 );
	}

	gun.LoadObject( "gun2.obj", "gun2.mtl" );

	InitializeTerrain();
	InitializeParticleSystem();
	
	return TRUE;												// Return TRUE (Initialization Successful)
}

void Deinitialize (void)										// Any User DeInitialization Goes Here
{
	glDeleteLists(base,95);										// Delete All 95 Font Display Lists

	FinalizeTerrain();
}

void Selection(void)											// This Is Where Selection Is Done
{
	GLuint	buffer[512];										// Set Up A Selection Buffer
	GLint	hits;												// The Number Of Objects That We Selected

	if (game)													// Is Game Over?
		return;													// If So, Don't Bother Checking For Hits
	
	PlaySound("data/shot.wav",NULL,SND_ASYNC);					// Play Gun Shot Sound

	// The Size Of The Viewport. [0] Is <x>, [1] Is <y>, [2] Is <length>, [3] Is <width>
	GLint	viewport[4];

	// This Sets The Array <viewport> To The Size And Location Of The Screen Relative To The Window
	glGetIntegerv(GL_VIEWPORT, viewport);
	glSelectBuffer(512, buffer);								// Tell OpenGL To Use Our Array For Selection

	// Puts OpenGL In Selection Mode. Nothing Will Be Drawn.  Object ID's and Extents Are Stored In The Buffer.
	(void) glRenderMode(GL_SELECT);

	glInitNames();												// Initializes The Name Stack
	glPushName(0);												// Push 0 (At Least One Entry) Onto The Stack

	glMatrixMode(GL_PROJECTION);								// Selects The Projection Matrix
	glPushMatrix();												// Push The Projection Matrix
	glLoadIdentity();											// Resets The Matrix

	// This Creates A Matrix That Will Zoom Up To A Small Portion Of The Screen, Where The Mouse Is.
	gluPickMatrix((GLdouble) mouse_x, (GLdouble) (viewport[3]-mouse_y), 1.0f, 1.0f, viewport);

	// Apply The Perspective Matrix
	gluPerspective(45.0f, (GLfloat) (viewport[2]-viewport[0])/(GLfloat) (viewport[3]-viewport[1]), 0.1f, 100.0f);
	glMatrixMode(GL_MODELVIEW);									// Select The Modelview Matrix
	DrawTargets();												// Render The Targets To The Selection Buffer
	glMatrixMode(GL_PROJECTION);								// Select The Projection Matrix
	glPopMatrix();												// Pop The Projection Matrix
	glMatrixMode(GL_MODELVIEW);									// Select The Modelview Matrix
	hits=glRenderMode(GL_RENDER);								// Switch To Render Mode, Find Out How Many
																// Objects Were Drawn Where The Mouse Was
	if (hits > 0)												// If There Were More Than 0 Hits
	{
		int	choose = buffer[3];									// Make Our Selection The First Object
		int depth = buffer[1];									// Store How Far Away It Is 

		for (int loop = 1; loop < hits; loop++)					// Loop Through All The Detected Hits
		{
			// If This Object Is Closer To Us Than The One We Have Selected
			if (buffer[loop*4+1] < GLuint(depth))
			{
				choose = buffer[loop*4+3];						// Select The Closer Object
				depth = buffer[loop*4+1];						// Store How Far Away It Is
			}       
		}

		if (!enemy[choose].hit)								// If The Object Hasn't Already Been Hit
		{
			AllocParticleHolder(enemy[choose].x,enemy[choose].y,enemy[choose].z);

			enemy[choose].hit=TRUE;							// Mark The Object As Being Hit
			score+=1;											// Increase Score
			kills+=1;											// Increase Level Kills
			if (kills>level*5)									// New Level Yet?
			{
				miss=0;											// Misses Reset Back To Zero
				kills=0;										// Reset Level Kills
				level+=1;										// Increase Level
				if (level>30)									// Higher Than 30?
					level=30;									// Set Level To 30 (Are You A God?)
			}
		}
    }
}

void Update(DWORD milliseconds)									// Perform Motion Updates Here
{
// 	if (g_keys->keyDown[' '] && game)							// Space Bar Being Pressed After Game Has Ended?
// 	{
// 		for (int loop=0; loop<30; loop++)							// Loop Through 30 Objects
// 			object[loop].Init();										// Initialize Each Object
// 
// 		game=FALSE;												// Set game (Game Over) To False
// 		score=0;												// Set score To 0
// 		level=1;												// Set level Back To 1
// 		kills=0;												// Zero Player Kills
// 		miss=0;													// Set miss (Missed Shots) To 0
// 	}

	roll-=milliseconds*0.00005f;								// Roll The Clouds

	for (int loop=0; loop<level; loop++)						// Loop Through The Objects
	{
		enemy[loop].Update(milliseconds);
	}
}

void Explosion(int num)											// Draws An Animated Explosion For Object "num"
{
	/*
	float ex = (float)((object[num].frame/4)%4)/4.0f;			// Calculate Explosion X Frame (0.0f - 0.75f)
	float ey = (float)((object[num].frame/4)/4)/4.0f;			// Calculate Explosion Y Frame (0.0f - 0.75f)

	glBindTexture(GL_TEXTURE_2D, textures[5].texID);			// Select The Explosion Texture
	glBegin(GL_QUADS);											// Begin Drawing A Quad
		glTexCoord2f(ex      ,1.0f-(ey      )); glVertex3f(-1.0f,-1.0f,0.0f);	// Bottom Left
		glTexCoord2f(ex+0.25f,1.0f-(ey      )); glVertex3f( 1.0f,-1.0f,0.0f);	// Bottom Right
		glTexCoord2f(ex+0.25f,1.0f-(ey+0.25f)); glVertex3f( 1.0f, 1.0f,0.0f);	// Top Right
		glTexCoord2f(ex      ,1.0f-(ey+0.25f)); glVertex3f(-1.0f, 1.0f,0.0f);	// Top Left
	glEnd();													// Done Drawing Quad
	*/

	enemy[num].frame+=1;										// Increase Current Explosion Frame
	if (enemy[num].frame>20)									// Have We Gone Through All 16 Frames?
	{
		enemy[num].Init();										// Init The Object (Assign New Values)
	}
}

void DrawTargets(void)											// Draws The Targets (Needs To Be Seperate)
{
	glLoadIdentity();											// Reset The Modelview Matrix
	//glTranslatef(0.0f,0.0f,-10.0f);								// Move Into The Screen 20 Units
	for (int loop=0; loop<level; loop++)						// Loop Through 9 Objects
	{
		glLoadName(loop);										// Assign Object A Name (ID)
		glPushMatrix();											// Push The Modelview Matrix
		if (enemy[loop].hit)									// If Object Has Been Hit
		{
			Explosion(loop);									// Draw An Explosion
		}
		else													// Otherwise
		{
			enemy[loop].Draw();	// Draw The Object
		}
		glPopMatrix();											// Pop The Modelview Matrix
	}
}

void drawSky() {
	glBindTexture(GL_TEXTURE_2D, textures[7].texID);			// Select The Sky Texture
	glBegin(GL_QUADS);											// Begin Drawing Quads
		glTexCoord2f(1.0f,roll/1.5f+1.0f); glVertex3f( 28.0f,+7.0f,-50.0f);	// Top Right
		glTexCoord2f(0.0f,roll/1.5f+1.0f); glVertex3f(-28.0f,+7.0f,-50.0f);	// Top Left
		glTexCoord2f(0.0f,roll/1.5f+0.0f); glVertex3f(-28.0f,-3.0f,-50.0f);	// Bottom Left
		glTexCoord2f(1.0f,roll/1.5f+0.0f); glVertex3f( 28.0f,-3.0f,-50.0f);	// Bottom Right

		glTexCoord2f(1.5f,roll+1.0f); glVertex3f( 28.0f,+7.0f,-50.0f);		// Top Right
		glTexCoord2f(0.5f,roll+1.0f); glVertex3f(-28.0f,+7.0f,-50.0f);		// Top Left
		glTexCoord2f(0.5f,roll+0.0f); glVertex3f(-28.0f,-3.0f,-50.0f);		// Bottom Left
		glTexCoord2f(1.5f,roll+0.0f); glVertex3f( 28.0f,-3.0f,-50.0f);		// Bottom Right

		glTexCoord2f(1.0f,roll/1.5f+1.0f); glVertex3f( 28.0f,+7.0f,0.0f);	// Top Right
		glTexCoord2f(0.0f,roll/1.5f+1.0f); glVertex3f(-28.0f,+7.0f,0.0f);	// Top Left
		glTexCoord2f(0.0f,roll/1.5f+0.0f); glVertex3f(-28.0f,+7.0f,-50.0f);	// Bottom Left
		glTexCoord2f(1.0f,roll/1.5f+0.0f); glVertex3f( 28.0f,+7.0f,-50.0f);	// Bottom Right

		glTexCoord2f(1.5f,roll+1.0f); glVertex3f( 28.0f,+7.0f,0.0f);		// Top Right
		glTexCoord2f(0.5f,roll+1.0f); glVertex3f(-28.0f,+7.0f,0.0f);		// Top Left
		glTexCoord2f(0.5f,roll+0.0f); glVertex3f(-28.0f,+7.0f,-50.0f);		// Bottom Left
		glTexCoord2f(1.5f,roll+0.0f); glVertex3f( 28.0f,+7.0f,-50.0f);		// Bottom Right
	glEnd();													// Done Drawing Quads
}

void drawGround() {
	glBindTexture(GL_TEXTURE_2D, textures[6].texID);			// Select The Ground Texture
	glBegin(GL_QUADS);											// Draw A Quad
		glTexCoord2f(7.0f,4.0f-roll); glVertex3f( 27.0f,-3.0f,-50.0f);	// Top Right
		glTexCoord2f(0.0f,4.0f-roll); glVertex3f(-27.0f,-3.0f,-50.0f);	// Top Left
		glTexCoord2f(0.0f,0.0f-roll); glVertex3f(-27.0f,-3.0f,0.0f);	// Bottom Left
		glTexCoord2f(7.0f,0.0f-roll); glVertex3f( 27.0f,-3.0f,0.0f);	// Bottom Right
	glEnd();													// Done Drawing Quad
}

void drawCrosshair()
{
	glPushMatrix();	
	// Crosshair (In Ortho View)
//	RECT window;												// Storage For Window Dimensions
//	GetClientRect (g_window->hWnd,&window);						// Get Window Dimensions
	glMatrixMode(GL_PROJECTION);								// Select The Projection Matrix
	glPushMatrix();												// Store The Projection Matrix
	glLoadIdentity();											// Reset The Projection Matrix
	glOrtho(0,window_x,0,window_y,-1,1);				// Set Up An Ortho Screen
	glMatrixMode(GL_MODELVIEW);									// Select The Modelview Matrix
	glTranslated(mouse_x, window_y-mouse_y,0.0f);			// Move To The Current Mouse Position

	glColor3f(1, 1, 1);

	float width = 16;
	float height = 16;
	GLuint texid = 8;
	glBindTexture(GL_TEXTURE_2D, textures[texid].texID);		// Select The Correct Texture
	glBegin(GL_QUADS);											// Start Drawing A Quad
		glTexCoord2f(0.0f,0.0f); glVertex3f(-width,-height,0.0f);	// Bottom Left
		glTexCoord2f(1.0f,0.0f); glVertex3f( width,-height,0.0f);	// Bottom Right
		glTexCoord2f(1.0f,1.0f); glVertex3f( width, height,0.0f);	// Top Right
		glTexCoord2f(0.0f,1.0f); glVertex3f(-width, height,0.0f);	// Top Left
	glEnd();													// Done Drawing Quad
	glPopMatrix();
}

void DrawGun()
{
	glPushMatrix();
	glTranslatef( 5.0f, -3.0f, 0.0f );

	glRotatef( 0.0, 0.0, 1.0, 0.0 );
	glRotatef( 10.0, 1.0, 0.0, 0.0 );

	glTranslatef( 0.0f, 0.0f, 5.0f );
	glRotatef( -52.0 * ( (double)mouse_y / (double)window_y ), 1.0, 0.0, 0.0 );
	glRotatef( 25, 1.0, 0.0, 0.0 );

	glRotatef( -65.0 * ( (double)mouse_x / (double)window_x ), 0.0, 1.0, 0.0 );
	glRotatef( 45, 0.0, 1.0, 0.0 );
	glRotatef( gunangle, 1.0, 0.0, 0.0 );
	glTranslatef( 0.0f, 0.0f, -5.0f );

	glRotatef( 90.0, 0.0, 1.0, 0.0 );
	glRotatef( 90.0, 1.0, 0.0, 0.0 );
	glScalef( 0.04f, 0.04f, 0.04f );
	gun.DrawObject();
	glPopMatrix();
}

void Draw(void)													// Draw Our Scene
{
	glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);		// Clear Screen And Depth Buffer
	glLoadIdentity();											// Reset The Modelview Matrix
	
	glPushMatrix();

	glColor3f(1, 1, 1);
	drawSky();
	//drawGround();

	glDisable(GL_DEPTH_TEST);
	glColor3f(0.5f, 0.3f, 0.3f);
	glDisable(GL_BLEND);
	DrawTerrain();
	glEnable(GL_BLEND);

	glEnable(GL_DEPTH_TEST);
	DrawTargets();												// Draw Our Targets

	DrawGun();

	glPopMatrix();
	
	DrawParticles();

	drawCrosshair();

	// Game Stats / Title
	glPrint(240,450,"Killer Turtle");						// Print Title
	glPrint(10,10,"Level: %i",level);							// Print Level
	glPrint(250,10,"Score: %i",score);							// Print Score

	if (miss>9)													// Have We Missed 10 Objects?
	{
		miss=9;													// Limit Misses To 10
		game=TRUE;												// Game Over TRUE
	}

	if (game)													// Is Game Over?
		glPrint(490,10,"GAME OVER");							// Game Over Message
	else
		glPrint(490,10,"Life: %i/10",10-miss);				// Print Morale #/10

	glMatrixMode(GL_PROJECTION);								// Select The Projection Matrix
	glPopMatrix();												// Restore The Old Projection Matrix
	glMatrixMode(GL_MODELVIEW);									// Select The Modelview Matrix

	glFlush();													// Flush The GL Rendering Pipeline
}
